#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os

from common.common import check_path_legal, is_clone_file_system
from common.util.exec_utils import exec_ln_cmd
from generaldb.gbase8a import LOGGER
from generaldb.gbase8a.commands.gbase_cmd import GbaseCmd
from generaldb.gbase8a.common.common_models import ModifyPermission
from generaldb.gbase8a.common.const import LinkTaskType, GbasePath, ErrorCode
from generaldb.gbase8a.common.file_operate import FileOperate
from generaldb.gbase8a.common.gbase_common import remove_file_dir_link, get_local_uuid
from generaldb.gbase8a.util.gbase_util import GbaseUtil


class CopyLink:

    def __init__(self, copy_id_list, data_area_path, os_user, link_task_type, param=None):
        self._copy_id_list = copy_id_list
        self._data_area_path = data_area_path
        self._os_user = os_user
        self._link_task_type = link_task_type
        self._copy_id = ''
        self._copy_cycle_file_list = []
        self._copy_gnode_file_list = []
        self._link_dir_list = []
        self._cycle_path_name = ''
        self._backupset_path_name = ''
        self._host_id = get_local_uuid()
        if param:
            ret = GbaseUtil.get_agent_id(param)
            if not isinstance(ret, ErrorCode):
                self._host_id = ret
        self._task_map = self.init_task_map()
        self._param = param

    @staticmethod
    def parse_dir(path):
        file_list = os.listdir(path)
        dir_list = []
        sub_file_list = []
        for file_name in file_list:
            file_path = os.path.join(path, file_name)
            if os.path.islink(file_path):
                continue
            elif os.path.isdir(file_path):
                dir_list.append(file_name)
            elif os.path.isfile(file_path):
                sub_file_list.append(file_name)
        return dir_list, sub_file_list

    def init_task_map(self):
        task_map = {
            LinkTaskType.BACKUP: [self.pre_link_path, self.pre_copy_gnode_file_path, self.construct_directory,
                                  self.create_link, self.change_permission],
            LinkTaskType.RESTORE: [self.pre_link_path, self.create_link, self.change_permission],
            LinkTaskType.DELLINK: [self.pre_link_path, self.del_link],
        }
        return task_map

    def init_permission_info(self, path, mode=None):
        result = ModifyPermission(path=path,
                                  user=self._os_user,
                                  mode=mode,
                                  path_check_func=check_path_legal,
                                  path_check_func_args=(path, GbasePath.GBASE_LINK_PATH))
        return result

    def pre_link_path(self):
        new_path = os.path.join(self._data_area_path, self._copy_id, self._host_id, self._backupset_path_name,
                                self._cycle_path_name)
        for old_copy_id in self._copy_id_list[:-1]:
            gnode_path = os.path.join(self._data_area_path, old_copy_id, self._host_id, self._backupset_path_name,
                                      self._cycle_path_name, "gnode")
            if not os.path.exists(gnode_path):
                LOGGER.error("Path not exists:%s", gnode_path)
                return False
            gnode_dir_list, gnode_file_list = self.parse_dir(gnode_path)
            if not gnode_dir_list:
                continue
            self._link_dir_list.append(
                (os.path.join(gnode_path, gnode_dir_list[0]), os.path.join(new_path, "gnode", gnode_dir_list[0])))
        return True

    def pre_copy_gnode_file_path(self):
        parents_copyid = self._copy_id_list[-2]
        if not parents_copyid:
            return False
        path_info = os.path.join(self._data_area_path, parents_copyid, self._host_id, self._backupset_path_name,
                                 self._cycle_path_name, "gnode")
        dir_list, sub_file_list = self.parse_dir(path_info)
        for file_name in sub_file_list:
            path = os.path.join(path_info, file_name)
            self._copy_gnode_file_list.append(path)
        return True

    def construct_directory(self):
        copyid = self._copy_id_list[-1]
        copy_path = os.path.join(self._data_area_path, copyid, self._host_id)
        backupset_path_info = os.path.join(copy_path, self._backupset_path_name)
        path_info = self.init_permission_info(backupset_path_info)
        if not FileOperate.mkdir_change_permission(path_info):
            LOGGER.error("Mdkir fail %s", backupset_path_info)
            return False
        cycle_path_info = os.path.join(backupset_path_info, self._cycle_path_name)
        path_info = self.init_permission_info(cycle_path_info)
        if not FileOperate.mkdir_change_permission(path_info):
            LOGGER.error("Mdkir fail %s", cycle_path_info)
            return False
        gnode_path_info = os.path.join(cycle_path_info, "gnode")
        path_info = self.init_permission_info(gnode_path_info)
        if not FileOperate.mkdir_change_permission(path_info):
            LOGGER.error("Mdkir fail %s", gnode_path_info)
            return False
        if not FileOperate.cp_file_list(self._host_id, self._copy_cycle_file_list, backupset_path_info):
            return False
        if self._copy_gnode_file_list:
            if not FileOperate.cp_file_list(self._host_id, self._copy_gnode_file_list, gnode_path_info):
                return False
        p_copyid = self._copy_id_list[-2]
        sre_path = os.path.join(self._data_area_path, p_copyid, self._host_id, self._backupset_path_name,
                                self._cycle_path_name, "gcluster")
        trg_path = os.path.join(self._data_area_path, copyid, self._host_id, self._backupset_path_name,
                                self._cycle_path_name)
        if not FileOperate.cp_dir(self._host_id, sre_path, trg_path):
            LOGGER.error("Cp dir fail ")
            return False
        return True

    def create_link(self):
        for sre_path, target_path in self._link_dir_list:
            if not exec_ln_cmd(sre_path, target_path):
                return False
        return True

    def get_cycle_path_name(self):
        parents_copyid = self._copy_id_list[-2]
        if not parents_copyid:
            return ''
        parents_copy_path = os.path.join(self._data_area_path, parents_copyid, self._host_id)
        path = os.path.join(parents_copy_path, self._backupset_path_name)
        dir_list, sub_file_list = self.parse_dir(path)
        if len(dir_list) != 1:
            return ''
        for file_name in sub_file_list:
            path_info = os.path.join(path, file_name)
            self._copy_cycle_file_list.append(path_info)
        return dir_list[0]

    def get_backupset_name(self):
        parents_copyid = self._copy_id_list[-2]
        if not parents_copyid:
            return ''
        parents_copy_path = os.path.join(self._data_area_path, parents_copyid, self._host_id)
        dir_list, _ = self.parse_dir(parents_copy_path)
        if len(dir_list) != 1:
            return ''
        return dir_list[0]

    def del_link(self):
        for _, target_path in self._link_dir_list:
            if not remove_file_dir_link(target_path):
                return False
        return True

    def change_permission(self):
        if self._link_task_type == "restore" and not is_clone_file_system(self._param.get_param_dict()):
            return True
        copy_path = os.path.join(self._data_area_path, self._copy_id, self._host_id, self._backupset_path_name)
        path_info = self.init_permission_info(copy_path, 755)
        if not GbaseCmd.recursion_modifying_permissions(path_info):
            LOGGER.error("Failed to modify the permission.")
            return False
        return True

    def main(self):
        if len(self._copy_id_list) < 2:
            return True
        self._copy_id = self._copy_id_list[-1]
        self._backupset_path_name = self.get_backupset_name()
        if not self._backupset_path_name:
            LOGGER.error("Get backupset path name fail")
            return False
        self._cycle_path_name = self.get_cycle_path_name()
        if not self._cycle_path_name:
            LOGGER.error("Get cycle path name fail")
            return False
        func_list = self._task_map.get(self._link_task_type)
        if not func_list:
            return False
        for func in func_list:
            ret = func()
            if not ret:
                LOGGER.error("Failed to execute the function %s.", func)
                return False
        return True
